#!/bin/bash
# -*- coding: utf-8 -*-
# --------------------------------------------------------
# Name:            ucf_team.py  
# Purpose:  基于已知的训练集，"测试集"中的user的item进行评分预测.
# Language:    Python 3.2
# --------------------------------------------------------
import heapq
from math import sqrt
import random

global Sim_U


##==================================
#         加载指定的训练集文件
#  参数fileName 代表某个训练集文件
##==================================
def loadMovieLensTrain(fileName='u1.base'):
    prefer = {}
    for line in open(fileName, 'r'):  # 打开指定文件
        (userid, movieid, rating, ts) = line.split('\t')  # 数据集中每行有4项
        prefer.setdefault(userid, {})  # 设置字典的默认格式,元素是user:{}字典
        prefer[userid][movieid] = float(rating)
    print("loadMovieLensTrain")
    return prefer  # 格式如{'user1':{itemid:rating, itemid2:rating, ,,}, {,,,}}


def loadMovieLensData(fileName='ml-100k/u.data'):
    prefer = {}
    for line in open(fileName, 'r'):  # 打开指定文件
        (userid, movieid, rating, ts) = line.split('\t')  # 数据集中每行有4项
        prefer.setdefault(userid, {})  # 设置字典的默认格式,元素是user:{}字典
        prefer[userid][movieid] = float(rating)
    print("loadMovieLensData")
    return prefer  # 格式如{'user1':{itemid:rating, itemid2:rating, ,,}, {,,,}}


##============================
# 对候选用户集的选取
# 获取每一位用户的候选用户集合
# ============================
def getSim_U(ISet, prefer):
    Sim_U = {}

    for person in ISet:  # 查找每一位用户的邻近候选用户集
        for userid in prefer:  # 从每一位用户中搜索  该用户userid评价过的项目是否在person的已评价过的项目组中
            for index in range(0, 21):
                if str(ISet[person][index][0]) in prefer[userid].keys():
                    x = 1
                    break
                else:
                    x = 0
            if x == 1:
                Sim_U.setdefault(person, {})
                Sim_U[person][userid] = userid
    print("getSim_U")
    return Sim_U


##============================
# 对项目组ISet的选取
# ============================
def getISet():
    m = 10  # 各个分段抽取10个项目
    ISet = {}
    prefer = {}
    movie = {}
    # file1=open('u.item','r')
    # file2=open('u.data','r')
    for line in open('ml-100k/u.data', 'r'):
        (userid, movieid, rating, ts) = line.strip().split('\t')  # 数据集中每行有4项
        prefer.setdefault(userid, {})  # 设置字典的默认格式,元素是user:{}字典
        prefer[userid][int(movieid)] = float(rating)
        movie[int(movieid)] = int(movieid)
    ##计算每个项目被评分次数
    item_time = {}
    u_unitem = {}  # 记录未被u评价过的项目
    # print(itemid+'  ')

    for item in movie.keys():
        item_time[item] = 0  # 设置项目item初始评价次数为0
        for userid in prefer:
            # print('未评价过'+str(item)+'项目的人')
            if item in prefer[userid].keys():  # 判断prefer[userid]是否存在item键
                item_time[item] = item_time[item] + 1  # 如果项目被用户评价过则将其被评价过的次数加1
                # print('评价过项目'+str(item)+'的人'+str(userid))
            else:  # 项目没有被userid用户评价过
                u_unitem.setdefault(userid, {})
                u_unitem[userid][item] = item  # userid未评价过的项目

    # 按从高到低排列评价次数
    # sorted(item_time.items(), key=lambda d: d[1], reverse=True)  # 由高到低排序
    # print(item_time)

    u_unitem_time = {}
    for person in u_unitem:
        for item in u_unitem[person]:
            u_unitem_time.setdefault(person, {})
            u_unitem_time[person][item] = item_time[item]
    UI_RH = []
    UI_RM = []
    UI_RL = []
    # file1=open('ISet.txt','a')
    for uid in prefer.keys():
        # 对该用户未评价过的项目 按照 其被评分次数降序排序
        u_unitem_time[uid] = sorted(u_unitem_time[uid].items(), key=lambda d: d[1], reverse=True)
        # 用户uid未评价过的项目里面选择高、中、低各一个
        threePart = int(len(u_unitem[uid]) / 3)
        for index2 in range(0, threePart):
            UI_RH.append(u_unitem_time[uid][threePart * 2 + index2])
            UI_RM.append(u_unitem_time[uid][threePart + index2])
            UI_RL.append(u_unitem_time[uid][index2])

            # ISet[uid][index]=u_unitem[uid][random.randint(0,int(threePart))]
            # ISet[uid][index+1]=u_unitem[uid][random.randint(int(threePart),2*int(threePart))]
            # ISet[uid][index+2]=u_unitem[uid][random.randint(2*int(threePart),int(threePart)*3)]
        ISet[uid] = random.sample(list(UI_RH), 7) + random.sample(list(UI_RM), 7) + random.sample(list(UI_RL), 7)
        # for index in range(0,len(ISet[uid])):
        # file1.write('%s\t%s\t%s\n'%(uid, ISet[uid][index][0], ISet[uid][index][1]))#按用户  项目  项目评分次数排列
    # file1.close()
    print("getISet")
    return ISet  # 随机 返回用户 未评价过的项目中 被评分次数高、中、低都有的组合项目返回


'''
查找未评分项目，并将其评分次数记下来：u.item和u.data结合，userID对应的未评分项目组
划分三个等级：高中低
从各个部分随机抽取10个项目
记录项目ID存入Iset中
'''


##==================================
#        加载对应的测试集文件
#  参数fileName 代表某个测试集文件,如u1.test
##==================================
def loadMovieLensTest(fileName='u1.test'):
    prefer = {}
    for line in open(fileName, 'r'):
        (userid, movieid, rating, ts) = line.split('\t')  # 数据集中每行有4项
        prefer.setdefault(userid, {})
        prefer[userid][movieid] = float(rating)
    print("loadMovieLensTest")
    return prefer


### 计算pearson相关度
def sim_pearson(prefer, person1, person2):
    sim = {}
    # 查找双方都评价过的项
    for item in prefer[person1]:
        if item in prefer[person2]:
            sim[item] = 1  # 将相同项添加到字典sim中
    # 元素个数
    n = len(sim)
    if len(sim) == 0:
        return -1

    # 所有偏好之和
    sum1 = sum([prefer[person1][item] for item in sim])
    sum2 = sum([prefer[person2][item] for item in sim])

    # 求平方和
    sum1Sq = sum([pow(prefer[person1][item], 2) for item in sim])
    sum2Sq = sum([pow(prefer[person2][item], 2) for item in sim])

    # 求乘积之和 ∑XiYi
    sumMulti = sum([prefer[person1][item] * prefer[person2][item] for item in sim])

    num1 = sumMulti - (sum1 * sum2 / n)
    num2 = sqrt((sum1Sq - pow(sum1, 2) / n) * (sum2Sq - pow(sum2, 2) / n))

    if num2 == 0:  ### 如果分母为0，本处将返回0.
        return 0

    result = num1 / num2

    return result


### 获取对item评分的K个最相似用户（K默认20）
def topKMatches(prefer, person, itemId, k=20, sim=sim_pearson):
    userSet = []
    # 找出所有Sim_U中评价过Item的用户,存入userSet
    for user in Sim_U:
        if itemId in str(Sim_U[person].keys()):
            userSet.append(user)
    # 计算相似性

    scores = [(sim(prefer, person, other), other) for other in Sim_U[person] if other != person]
    # print("sim_pearson->scores")
    # 按相似度排序
    rs = heapq.nlargest(k, scores)  # 用最大堆求最大的k个元素
    # print("topKMatches")
    return rs


### 计算用户的平均评分
def getAverage(prefer, userId):
    count = 0
    sum = 0
    for item in prefer[userId]:
        sum = sum + prefer[userId][item]
        count = count + 1
    # print("getAverage")
    return sum / count


### 平均加权策略，预测userId对itemId的评分
def getRating(prefer1, userId, itemId, knumber=20):
    jiaquanAverage = 0.0
    simSums = 0.0

    # 获取K近邻用户(评过分的用户集)
    users = topKMatches(prefer1, userId, itemId, k=knumber, sim=sim_pearson)

    # 获取userId 的平均值
    averageOfUser = getAverage(prefer1, userId)

    # 计算每个用户的加权，预测
    for other in users:
        sim = other[0]  # 与other其他用户的相似度
        otherId = other[1]
        averageOther = getAverage(prefer1, otherId)  # 该用户的平均分
        # 累加
        simSums += abs(sim)  # 取绝对值
        if itemId in prefer1[otherId].keys():
            jiaquanAverage += (prefer1[otherId][itemId] - averageOther) * sim  # 累加，一些值为负

    # simSums为0，即该项目尚未被其他用户评分，这里的处理方法：返回用户平均分
    if simSums == 0:
        return averageOfUser
    else:
        return averageOfUser + jiaquanAverage / simSums


##==================================================================
##     getAllUserRating(): 获取所有用户的预测评分，存放到fileResult中
##
## 参数:fileTrain,fileTest 是训练文件和对应的测试文件，fileResult为结果文件
##     similarity是相似度度量方法，默认是皮尔森。
##==================================================================
def getAllUserRating(fileTrain, fileTest, fileResult):
    preferTrain = loadMovieLensTrain(fileTrain)  # 加载训练集
    preferTest = loadMovieLensTest(fileTest)  # 加载测试集
    total = 0
    file2 = open(fileResult, 'a')
    for userid in preferTest:  # test集中每个用户
        for item in preferTest[userid]:  # 对于test集合中每一个项目用base数据集,CF预测评分
            rating = getRating(preferTrain, userid, item, 50)  # 基于训练集预测用户评分(用户数目<=K)
            if rating > getAverage(preferTrain, userid):
                file2.write('%s\t%s\t%s\t%s\n' % (userid, item, rating, preferTest[userid][item]))
                total += 1
    file2.close()
    print("-------------Completed!!---append to %s  %d 条数据" % (file2.name, total))


# ISet = getISet()
# fileTrain = 'u1.base'
# prefer1 = loadMovieLensTrain(fileTrain)
# Sim_U = getSim_U(ISet, prefer1)


############    主程序   ##############
if __name__ == "__main__":
    print(u"\n--------------start... -----------\n")
    ISet = getISet()
    for i in range(1, 6):
        baseName = 'u' + str(i) + '.base'
        testName = 'u' + str(i) + '.test'
        prefer1 = loadMovieLensTrain(baseName)
        Sim_U = getSim_U(ISet, prefer1)
        getAllUserRating('ml-100k/' + baseName, 'ml-100k/' + testName, 'recommend/recommend-item.txt')
    print(u"\n--------------end... -----------\n")

    '''
    
    prefer={}
    uid=11
    prefer.setdefault(uid,{})
    prefer[uid][11]=00
    

    userid='0'
    count='1'
    item='2'
    print('用户'+userid+'未评价过的第'+count+'个项目'+item)

    fileName='testrating50.txt ';
    fr=open(fileName);
    index=0
    prefer={}
    ## 
    
    ##   第2步逐行读取:
    delim='\t';
    line0=fr.readlines();
    

    ##   接着进行使用strip和split处理:
    while index<len(line0)-1:
        index=index+1
        line1=line0[index].strip()   
        line2=line0[index].strip().split(delim)

    prefer[0]=10
    prefer[1]=2
   '''
